//
//  Copyright 2011  comphilip
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using Dict4Engkoo.Engkoo;
using Dict4Engkoo.Result;

namespace Dict4Engkoo.Command {
	internal class DefineCommand : IAsyncCommand {
		private IResult _result = null;
		private DictContext _dictContext = null;
		private Action<IResult> _resultDelegate = null;
		private HttpWebRequest _request = null;
		
		public IResult Process(DictContext Context) {
			return EndProcess(BeginProcess(Context, null, null));
		}

		public IAsyncResult BeginProcess(DictContext Context, AsyncCallback Callback, object State) {
			_resultDelegate = SetResult;
			_dictContext = Context;
			if (Context.CommandParameters.Length != 2)
				return _resultDelegate.BeginInvoke(CommonResults.IllegalParameters, Callback, State);

			string[] validDbs = new string[] {"engkoo", "!", "*"};
			if (!Array.Exists(validDbs, (x) => string.Equals(x, Context.CommandParameters[0], StringComparison.OrdinalIgnoreCase)))
				return _resultDelegate.BeginInvoke(new StatusTextResponse(550, StatusArgument.CreateRawStringArgument(Strings.InvalidDatabase)), Callback, State);

			_resultDelegate = null;
			return BeginGetDefineFromEngkoo(Context.CommandParameters[1], Callback, State);
		}

		public IResult EndProcess(IAsyncResult AR) {
			if (_resultDelegate == null && _request == null)
				throw new InvalidOperationException();
			try{
				if (_resultDelegate != null){
					_resultDelegate.EndInvoke(AR);
					return _result;
				}else{
					IResult result = EndGetDefineFromEngkoo(AR);
					if (result is StatusResponse)
						return result;
					else
						return new CompositeResult {
							new StatusTextResponse(150,
								StatusArgument.CreateIntegerArgument(1),
								StatusArgument.CreateRawStringArgument(Strings.DefinitionsRetrievedStatusText)),
							new StatusTextResponse(151, StatusArgument.CreateQuoteStringArgument(_dictContext.CommandParameters[1]),
								StatusArgument.CreateRawStringArgument("engkoo"),
								StatusArgument.CreateQuoteStringArgument("engkoo")),
							result,
							CommonResults.OK
						};
				}
			}finally{
				_request = null;
				_result = null;
				_resultDelegate = null;
				_dictContext = null;
			}
		}

		private IAsyncResult BeginGetDefineFromEngkoo(string Word, AsyncCallback Callback, object State) {
			HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://dict.bing.com.cn/io.aspx");
			request.Method = "POST";
			request.ContentType = "application/x-www-form-urlencoded";
			using(StreamWriter requestWriter = new StreamWriter(request.GetRequestStream())) {
				requestWriter.Write(string.Format(CultureInfo.InvariantCulture,
												  "q={0}&t=dict&ut=default&ulang=ZH-CN&tlang=EN-US",
												  Uri.EscapeDataString(Word)));
			}
			_request = request;
			return request.BeginGetResponse(Callback, State);
		}
		
		private IResult EndGetDefineFromEngkoo(IAsyncResult AR) {
			using(WebResponse response = _request.EndGetResponse(AR))
			using(Stream stream = response.GetResponseStream())
			using(StreamReader reader = new StreamReader(stream)) {
				EngkooQueryResult result = new EngkooQueryResult(reader.ReadToEnd());
				if (result.HasResult) {
					StringBuilder buffer = new StringBuilder();
					using(StringWriter buffWriter = new StringWriter(buffer)) {
						EngkooResultFormator.FormatResult(buffWriter, result);
					}
					return new TextResponse(buffer.ToString());
				} else
					return new StatusTextResponse(552, StatusArgument.CreateRawStringArgument(Strings.NoMatchStatusText));
			}
		}
		
		private void SetResult(IResult Result){
			_result = Result;	
		}
	}
}

